package client;

import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

import shared.AccountData;
import shared.BalanceData;
import shared.CollectionData;
import shared.Data;
import shared.LoginData;
import shared.SharedData;

/**
 * class Connection handles all connections from the client to the server
 * 
 * @author Rasmus Stjerne, Joachim Lindholm, Daniel Gramstrup, Pamela Pedersen
 */
public class Connection {

	private final int serverPort = 7896;
	private ObjectOutputStream oos;
	private ObjectInputStream ois;
	private Socket s;
	private SharedData sd;

	/**
	 * creates a connection from client to server
	 * 
	 * @throws IOException
	 */
	public void connect(String serverIP) {
		try {
			this.s = new Socket(serverIP, serverPort);
			oos = new ObjectOutputStream(s.getOutputStream());
			ois = new ObjectInputStream(s.getInputStream());
		} catch (IOException ioe) {
			ioe.printStackTrace();
		}

	}

	public void testConnection(String serverIP) throws IOException {
		try {
			this.s = new Socket(serverIP, serverPort);
			oos = new ObjectOutputStream(s.getOutputStream());
			oos.flush();
		} finally {
			s.close();
		}

	}

	/**
	 * Authenticates the user. Calculations are all made on the server-side.
	 * 
	 * @param accountName
	 *            of the user logging in
	 * @param accountPassword
	 *            of the user logging in
	 * @return boolean to represent if user is authenticated
	 */
	public boolean login(String accountName, String accountPassword) {
		sd = new SharedData();
		sd.accountName = Data.xor(accountName);
		sd.accountPassword = Data.xor(accountPassword);
		sd.transactionID = Data.xor(4);
		writeToServer();
		// receive from server
		LoginData ld = (LoginData) readFromServer();
		return ld.userAuthenticated;
	}

	/**
	 * Insert CC to the users account
	 * 
	 * @param amount
	 *            is the amount to insert on the account
	 */
	public void insert(double amount) {
		sd = new SharedData();
		sd.transactionID = Data.xor(3);
		sd.amount = Data.xor(amount);
		writeToServer();
	}

	/**
	 * Spend CC from the account logged in.
	 * 
	 * @param amount
	 *            the amount to spend
	 */
	public void spend(double amount) {
		sd = new SharedData();
		sd.transactionID = Data.xor(2);
		sd.amount = Data.xor(amount);

		writeToServer();
	}

	/**
	 * Sends a new transaction
	 * 
	 * @param recipient
	 *            is the user to receive the transaction
	 * @param amount
	 *            is the amount the recipient receives
	 */
	public void sendTransaction(String recipient, double amount) {
		sd = new SharedData();
		sd.recipient = Data.xor(recipient);
		sd.transferAmount = Data.xor(amount);
		sd.transactionID = Data.xor(8);
		writeToServer();
	}

	/**
	 * Accepts a selected transaction
	 * 
	 * @param selection
	 *            corresponds to the selected transaction
	 */
	public void acceptTransaction(int selection) {
		sd = new SharedData();
		sd.transactionID = Data.xor(10);
		sd.selection = Data.xor(selection);
		writeToServer();
	}

	/**
	 * Gets balance using the BalanceData object
	 * 
	 * @return BalanceData object
	 */
	public BalanceData viewBalance() {
		sd = new SharedData();
		sd.transactionID = Data.xor(1);
		writeToServer();
		// receive from server
		BalanceData bd = (BalanceData) readFromServer();
		bd.balance = Data.xor(bd.balance);
		bd.pendingBalance = Data.xor(bd.pendingBalance);
		bd.totalBalance = Data.xor(bd.totalBalance);
		return bd;
	}

	/**
	 * Gets the limit the user can transfer
	 * 
	 * @return transactionLimit
	 */
	public double transactionLimit() {
		sd = new SharedData();
		sd.transactionID = Data.xor(12);
		writeToServer();
		// receive from server
		double transactionLimit = (Double) readFromServer();
		return Data.xor(transactionLimit);
	}

	/**
	 * Gets pending transactions for a user
	 * 
	 * @return Collection Data object
	 */
	public CollectionData getPendingTransactions() {
		sd = new SharedData();
		sd.transactionID = Data.xor(9);
		writeToServer();
		// receive from server
		CollectionData cd = (CollectionData) readFromServer();
		cd.xorArraylist();
		return cd;
	}

	/**
	 * Gets the CCKMFactor for when a user inserts CC
	 * 
	 * @return the CCKMFACTOR
	 */
	public double getCCKMFactor() {
		sd = new SharedData();
		sd.transactionID = Data.xor(5);
		writeToServer();
		// receive from server
		double CCKMFACTOR = (double) readFromServer();
		return Data.xor(CCKMFACTOR);
	}

	/**
	 * Gets the name of a user
	 * 
	 * @param accountName
	 *            of the user
	 * @return name of the user
	 */
	public String getName(String accountName) {
		sd = new SharedData();
		sd.accountName = Data.xor(accountName);
		sd.transactionID = Data.xor(7);
		writeToServer();
		// receive from server
		String name = readFromServer().toString();
		return Data.xor(name);
	}

	/**
	 * Gets the CCUSEFACTOR
	 * 
	 * @return the CCUSEFACTOR
	 */
	public double getCCUSEFACTOR() {
		sd = new SharedData();
		sd.transactionID = Data.xor(6);
		writeToServer();
		// receive from server
		double CCUSEFACTOR = (double) readFromServer();
		return Data.xor(CCUSEFACTOR);
	}

	/**
	 * Declines a selected transaction
	 * 
	 * @param selection
	 *            corresponds to the selected transaction
	 */
	public void declineTransaction(int selection) {
		sd = new SharedData();
		sd.transactionID = Data.xor(11);
		sd.selection = Data.xor(selection);
		writeToServer();
	}

	/**
	 * Logs the user out of the system and closes the connection
	 */
	public void logout() {
		sd = new SharedData();
		sd.transactionID = Data.xor(-1);
		try {
			writeToServer();
		} finally {
			if (s != null)
				try {
					s.close();
				} catch (IOException e) {
					System.out.println("close:" + e.getMessage());
				}
		}
	}

	/**
	 * Checks whether or not the account exists
	 * 
	 * @param recipient
	 * @return boolean to represent if the account exists or not
	 */
	public boolean checkAccountExists(String recipient) {
		sd = new SharedData();
		sd.transactionID = Data.xor(13);
		sd.recipient = Data.xor(recipient);
		writeToServer();
		boolean accountExists = ((AccountData) readFromServer()).userExists;
		return accountExists;
	}

	/**
	 * method to write to the server. this method uses the ObjectOutputStream
	 * and writes to the server through the connection established in the
	 * connect() method.
	 */
	public void writeToServer() {
		try {

			oos.writeObject(sd);

		} catch (UnknownHostException e) {
			System.out.println("Sock:" + e.getMessage());
		} catch (EOFException e) {
			System.out.println("EOF:" + e.getMessage());
		} catch (IOException e) {
			System.out.println("IO:" + e.getMessage());
		}
	}

	/**
	 * method to read from the server. this method uses the ObjectInputStream
	 * and reads from the server through the connection established in the
	 * connect() method.
	 * 
	 * @return Object output
	 */
	public Object readFromServer() {
		Object output = null;
		try {

			output = ois.readObject();

		} catch (UnknownHostException e) {
			System.out.println("Sock:" + e.getMessage());
		} catch (EOFException e) {
			System.out.println("EOF:" + e.getMessage());
		} catch (IOException e) {
			System.out.println("IO:" + e.getMessage());
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return output;
	}
}